﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Management;

namespace PlugMeterControl
{
    public partial class FormOpenPort : Form
    {
        private const int INVALID_HANDLE_VALUE = -1;
        private const int DIGCF_DEFAULT = 0x00000001;
        private const int DIGCF_PRESENT = 0x00000002;
        private const int DIGCF_ALLCLASSES = 0x00000004;
        private const int DIGCF_PROFILE = 0x00000008;
        private const int DIGCF_DEVICEINTERFACE = 0x00000010;
        private const int REG_NONE = 0x00000000;
        private const int REG_SZ = 0x00000001;
        private const int REG_EXPAND_SZ = 0x00000002;
        private const int REG_BINARY = 0x00000003;
        private const int REG_DWORD = 0x00000004;
        private const int REG_DWORD_LITTLE_ENDIAN = 0x00000004;
        private const int REG_DWORD_BIG_ENDIAN = 0x00000005;
        private const int REG_LINK = 0x00000006;
        private const int REG_MULTI_SZ = 0x00000007;
        private const int SPDRP_DEVICEDESC = 0x00000000;
        private const int SPDRP_HARDWAREID = 0x00000001;
        private const int SPDRP_COMPATIBLEIDS = 0x00000002;
        private const int SPDRP_NTDEVICEPATHS = 0x00000003;
        private const int SPDRP_SERVICE = 0x00000004;
        private const int SPDRP_CONFIGURATION = 0x00000005;
        private const int SPDRP_CONFIGURATIONVECTOR = 0x00000006;
        private const int SPDRP_CLASS = 0x00000007;
        private const int SPDRP_CLASSGUID = 0x00000008;
        private const int SPDRP_DRIVER = 0x00000009;
        private const int SPDRP_CONFIGFLAGS = 0x0000000A;
        private const int SPDRP_MFG = 0x0000000B;
        private const int SPDRP_FRIENDLYNAME = 0x0000000C;
        private const int SPDRP_LOCATION_INFORMATION = 0x0000000D;
        private const int SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E;
        private const int SPDRP_CAPABILITIES = 0x0000000F;
        private const int SPDRP_UI_NUMBER = 0x00000010;
        private const int SPDRP_UPPERFILTERS = 0x00000011;
        private const int SPDRP_LOWERFILTERS = 0x00000012;
        private const int SPDRP_MAXIMUM_PROPERTY = 0x00000013;
        private const int DICS_FLAG_GLOBAL = 0x00000001;
        private const int DIREG_DEV = 0x00000001;
        private const int KEY_READ = 0x00020019;


        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct SP_DEVINFO_DATA
        {
            public int cbSize;
            public Guid ClassGuid;
            public IntPtr DevInst;
            public int Reserved;
        }

        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool SetupDiClassGuidsFromName(string ClassName, ref Guid ClassGuidArray1stItem, UInt32 ClassGuidArraySize, out UInt32 RequiredSize);
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, int Flags);
        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out UInt32 PropertyRegDataType,
                                                                   IntPtr PropertyBuffer, uint PropertyBufferSize, out UInt32 RequiredSize);
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern UIntPtr SetupDiOpenDevRegKey(IntPtr hDeviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int scope, int hwProfile,
                                                         int parameterRegistryValueKind, int samDesired);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
        public static extern int RegQueryValueEx(UIntPtr hKey, string lpValueName, int lpReserved, out uint lpType, System.Text.StringBuilder lpData, ref uint lpcbData);
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
        public static extern int RegCloseKey(UIntPtr hKey);

        public FormOpenPort()
        {
            InitializeComponent();

            comboBoxBaudRate.Items.Add("110");
            comboBoxBaudRate.Items.Add("300");
            comboBoxBaudRate.Items.Add("1200");
            comboBoxBaudRate.Items.Add("2400");
            comboBoxBaudRate.Items.Add("4800");
            comboBoxBaudRate.Items.Add("9600");
            comboBoxBaudRate.Items.Add("19200");
            comboBoxBaudRate.Items.Add("38400");
            comboBoxBaudRate.Items.Add("57600");
            comboBoxBaudRate.Items.Add("115200");
            comboBoxBaudRate.Items.Add("230400");
            comboBoxBaudRate.Items.Add("460800");
            comboBoxBaudRate.Items.Add("921600");
            comboBoxBaudRate.SelectedIndex = 9; // baudrate 115200

            comboBoxDataBits.Items.Add("5");
            comboBoxDataBits.Items.Add("6");
            comboBoxDataBits.Items.Add("7");
            comboBoxDataBits.Items.Add("8");
            comboBoxDataBits.SelectedIndex = 3; // 8 data bits

            comboBoxParity.Items.Add("Even");
            comboBoxParity.Items.Add("Mark");
            comboBoxParity.Items.Add("None");
            comboBoxParity.Items.Add("Odd");
            comboBoxParity.Items.Add("Space");
            comboBoxParity.SelectedIndex = 2;  // Parity None

            comboBoxStopBits.Items.Add("None");
            comboBoxStopBits.Items.Add("One");
            comboBoxStopBits.Items.Add("OnePointFive");
            comboBoxStopBits.Items.Add("Two");
            comboBoxStopBits.SelectedIndex = 1; // 1 stop bit

            comboBoxFlowControl.Items.Add("None");
            comboBoxFlowControl.Items.Add("RequestToSend");
            comboBoxFlowControl.Items.Add("RequestToSendXOnXOff");
            comboBoxFlowControl.Items.Add("XOnXOff");
            comboBoxFlowControl.SelectedIndex = 0; // Flowcontrol None
        }

        private void buttonOpenPortCancel_Click(object sender, EventArgs e)
        {
            Hide();
        }

        private void buttonOpenPortOK_Click(object sender, EventArgs e)
        {
            if (PortShowFancyNames)
            {
                PortName = comboBoxPortName.Text.Substring(comboBoxPortName.Text.IndexOf("COM"), comboBoxPortName.Text.IndexOf(')') - comboBoxPortName.Text.IndexOf("COM"));
            }
            else
            {
                PortName = comboBoxPortName.Text;
            }
            PortBaudRate = comboBoxBaudRate.Text;
            PortDataBits = comboBoxDataBits.Text;
            PortParity = comboBoxParity.Text;
            PortStopbits = comboBoxStopBits.Text;
            PortHandshake = comboBoxFlowControl.Text;
            Hide();
        }

        private void FormOpenPort_VisibleChanged(object sender, EventArgs e)
        {
            if (buttonOpenPortOK.Enabled)
            {
                buttonOpenPortOK.Enabled = false;
            }
            else
            {
                bool portfound = false;

                comboBoxPortName.Items.Clear();
                comboBoxPortName.Text = "";
                if (PortShowFancyNames)
                {
                    try
                    {
                        UInt32 RequiredSize = 0;
                        Guid[] GuidArray = new Guid[1];
                        IntPtr DeviceInfoSet;
                        SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA();
                        UInt32 dwMemberIndex;

                        DeviceInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
                        // read Guids
                        bool Status = SetupDiClassGuidsFromName("PORTS", ref GuidArray[0], 1, out RequiredSize);
                        if (true == Status)
                        {
                            DeviceInfoSet = SetupDiGetClassDevs(ref GuidArray[0], IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);
                            if (DeviceInfoSet.ToInt32() != INVALID_HANDLE_VALUE)
                            {
                                dwMemberIndex = 0;

                                while (SetupDiEnumDeviceInfo(DeviceInfoSet, dwMemberIndex++, ref DeviceInfoData))
                                {
                                    IntPtr szFriendlyName = new IntPtr();
                                    szFriendlyName = Marshal.AllocHGlobal(1024);
                                    StringBuilder szPortName = new StringBuilder();
                                    UInt32 dwReqSize = 0;
                                    UInt32 dwPropType;
                                    uint dwType = REG_SZ;
                                    UIntPtr hKey;

                                    // Get Friendly name
                                    Status = SetupDiGetDeviceRegistryProperty(DeviceInfoSet, ref DeviceInfoData, SPDRP_FRIENDLYNAME, out dwPropType, szFriendlyName, 1024, out dwReqSize);

                                    // Open device parameters reg key
                                    hKey = SetupDiOpenDevRegKey(DeviceInfoSet, ref DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);

                                    if (hKey.ToUInt32() != 0xFFFFFFFF)
                                    {
                                        // Query for portname
                                        int lRet;

                                        dwReqSize = 1024;
                                        lRet = RegQueryValueEx(hKey, "PortName", 0, out dwType, szPortName, ref dwReqSize);
                                        // Close ref key
                                        // RegCloseKey(hKey); // fails in debugger runs oke in execuable
                                        if (szPortName.ToString().Contains("COM"))
                                        {
                                            string FriendlyName = Marshal.PtrToStringAuto(szFriendlyName);
                                            comboBoxPortName.Items.Add(FriendlyName);
                                            if (szPortName.ToString().Equals(PortName))
                                            {
                                                comboBoxPortName.Text = FriendlyName;
                                                comboBoxPortName.SelectedValue = FriendlyName;
                                                portfound = true;
                                            }
                                            buttonOpenPortOK.Enabled = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (UnauthorizedAccessException)
                    {
                        PortShowFancyNames = false;
                    }
                }
                if (!PortShowFancyNames)
                {
                    foreach (String s in SerialPort.GetPortNames())
                    {
                        comboBoxPortName.Items.Add(s);
                        if (PortName == s)
                        {
                            comboBoxPortName.Text = s;
                            comboBoxPortName.SelectedValue = s;
                            portfound = true;
                        }
                        buttonOpenPortOK.Enabled = true;
                    }
                }
                if (!portfound)
                {
                    comboBoxPortName.SelectedIndex = 0;
                }
            }
        }
    }
}
